import json
from pptx import Presentation
from pptx.enum.shapes import MSO_SHAPE_TYPE
from pptx.enum.text import PP_PARAGRAPH_ALIGNMENT
from pptx.dml.color import RGBColor

def color_to_hex(color):
    try:
        if color and isinstance(color.rgb, RGBColor):
            return f"#{color.rgb:06X}"
        if color and color.rgb:
            return f"#{int(color.rgb):06X}"
    except Exception:
        pass
    return None

def para_align_to_str(alignment):
    mapping = {
        PP_PARAGRAPH_ALIGNMENT.LEFT: "left",
        PP_PARAGRAPH_ALIGNMENT.CENTER: "center",
        PP_PARAGRAPH_ALIGNMENT.RIGHT: "right",
        PP_PARAGRAPH_ALIGNMENT.JUSTIFY: "justify",
        PP_PARAGRAPH_ALIGNMENT.DISTRIBUTE: "distribute",
    }
    return mapping.get(alignment, "unknown")

def get_text_style(shape):
    style = []
    if not shape.has_text_frame:
        return style
    for p in shape.text_frame.paragraphs:
        p_info = {"alignment": para_align_to_str(p.alignment), "runs": []}
        for r in p.runs:
            run_info = {
                "text": r.text,
                "font_name": getattr(r.font, "name", None),
                "font_size_pt": getattr(r.font.size, "pt", None) if r.font.size else None,
                "bold": r.font.bold,
                "italic": r.font.italic,
                "underline": r.font.underline,
                "font_color": color_to_hex(r.font.color),
            }
            p_info["runs"].append(run_info)
        style.append(p_info)
    return style

def get_placeholder_type(shape):
    try:
        if shape.is_placeholder:
            phf = shape.placeholder_format
            return str(phf.type)  # e.g. PP_PLACEHOLDER.TITLE
    except Exception:
        pass
    return None

def shape_basic_info(shape):
    info = {
        "name": shape.name,
        "shape_type": str(shape.shape_type),  # MSO_SHAPE_TYPE
        "left": shape.left if hasattr(shape, "left") else None,
        "top": shape.top if hasattr(shape, "top") else None,
        "width": shape.width if hasattr(shape, "width") else None,
        "height": shape.height if hasattr(shape, "height") else None,
        "is_placeholder": bool(getattr(shape, "is_placeholder", False)),
        "placeholder_type": get_placeholder_type(shape),
    }
    # Text
    if getattr(shape, "has_text_frame", False):
        info["text_preview"] = shape.text
        info["text_style"] = get_text_style(shape)
    # Picture
    if shape.shape_type == MSO_SHAPE_TYPE.PICTURE:
        try:
            info["image_placeholder"] = True
        except Exception:
            info["image_placeholder"] = True
    # Table
    if shape.shape_type == MSO_SHAPE_TYPE.TABLE or getattr(shape, "has_table", False):
        info["has_table"] = True
        tbl = shape.table
        info["table_rows"] = len(tbl.rows)
        info["table_cols"] = len(tbl.columns)
    return info

def extract_template(pptx_path, output_json="template_info.json"):
    prs = Presentation(pptx_path)
    result = {
        "slides_count": len(prs.slides),
        "slide_masters": [sm.name for sm in prs.slide_masters],
        "slide_layouts": [sl.name for sl in prs.slide_layouts],
        "slides": [],
    }
    for idx, slide in enumerate(prs.slides, start=1):
        slide_info = {
            "index": idx,
            "layout_name": getattr(slide.slide_layout, "name", None),
            "shapes": [],
        }
        for shape in slide.shapes:
            slide_info["shapes"].append(shape_basic_info(shape))
        result["slides"].append(slide_info)

    with open(output_json, "w", encoding="utf-8") as f:
        json.dump(result, f, ensure_ascii=False, indent=2)
    print(f"Template info written to {output_json}")

if __name__ == "__main__":
    import argparse
    parser = argparse.ArgumentParser(description="Extract PPTX template info")
    parser.add_argument("pptx", help="Path to template .pptx")
    parser.add_argument("--out", default="template_info.json", help="Output JSON path")
    args = parser.parse_args()
    extract_template(args.pptx, args.out)
